import { toJS, set, makeAutoObservable } from 'mobx';
import { persist } from 'mobx-persist';
import isEqual from 'lodash/isEqual';
import cloneDeep from 'lodash/cloneDeep';
import moment from 'moment';
import global from '@/utils/global';
import { alramInfoSort, isSameAlarm, getDeviceId, isSameDevice, sleep } from '@/utils';
import Taro from '@tarojs/taro';
import ProductConfig, {
  IProduct,
  getItemForPk,
  IAlarm,
  haveDeviceTimeCorrect,
  IConvenientStateCondition,
} from '@/config/product';
import languageModel from '@/models/language';
import SDK from '@/utils/SDK';
import { logPage } from '@/services/record';
import deviceDetailModel from './deviceDetail';
import gizwitsSdk from './gizwitsSdk';
import { checkoutDeviceNeedConnect } from './deviceList';
import evaluateBooleanExpression from '@/utils/evaluateBooleanExpression/evaluateBooleanExpression';
import templateRouter from '@/templates/subPackages.json';
import alarmPush from './alarmPush';

const defaultTemplateRoute = 'com.gizwits.wx.ai';
export const pageRegExp: RegExp = /^pages\/{1}/;
export const templatesRegExp: RegExp = /^templates\/com{1}/;

interface IData extends IDeviceData {
  alarmInfo?: IAlarm[];
}

interface IReportPayload extends IData {
  device: IDevice;
  fromCmd?: boolean;
}

function filterDeviceData(
  currentData: IData,
  device: IDevice,
  data: any,
  alerts: any,
  faults: any
): { [deviceId: string]: IData } {
  if (!device) {
    return {};
  }
  // 兼容处理
  if (!currentData) {
    currentData = {
      data: {},
      alerts: {},
      faults: {},
    };
  }
  return {
    [getDeviceId(device)]: {
      data: { ...(currentData.data || {}), ...(data || {}) },
      alerts: { ...(currentData.alerts || {}), ...(alerts || {}) },
      faults: { ...(currentData.faults || {}), ...(faults || {}) },
      updateTime: new Date().getTime(),
    },
  };
}

// 转为boolean
function compare<T>(left: T, right: T, operator: IConvenientStateCondition['opt']): boolean {
  console.log(left, right, operator, 'compare');
  switch (operator) {
    case '<':
      return left < right;
    case '>':
      return left > right;
    case '!=':
      return left !== right;
    case '<=':
      return left <= right;
    case '==':
      return left == right;
    case '>=':
      return left >= right;
  }
}

// 条件满足判断
function evalConditions(data: any, conditions: any, logic: any) {
  let expression = logic;
  // tslint:disable-next-line:prefer-for-of
  for (let index = 0; index < conditions.length; index++) {
    const condition = conditions[index];
    const { name, left, opt, right } = condition;
    // const conditionExpression = `${data[left]} ${opt} ${right}`;
    // expression = expression.replace(name, conditionExpression);
    const conditionExpression = compare(data[left], right, opt);
    expression = expression.replace(name, conditionExpression);
  }
  let result = false;
  // 如果有变量没找到会出错
  try {
    // tslint:disable-next-line:no-eval
    // eslint-disable-next-line no-eval
    if (evaluateBooleanExpression(expression)) {
      result = true;
    }
  } catch (error) {
    console.log(error);
  }
  return result;
}

// 根据告警配置来判断设备上报数据是否发生告警
function getAlarmInfo(alarmConfig: any, { data, alerts, faults }: IData, device: IDevice) {
  const alarmInfoList = [];
  const { alarms } = alarmConfig;
  // alarms 先按告警类型，告警等级从高到低排序好的数组
  const deviceData = { ...data, ...alerts, ...faults };
  // tslint:disable-next-line:prefer-for-of
  for (let index = 0; index < alarms.length; index++) {
    const alarmItem = alarms[index];
    const { conditions, logic } = alarmItem;
    const result = evalConditions(deviceData, conditions, logic);
    if (result) {
      const {
        // eslint-disable-next-line no-shadow
        action,
        type,
        name,
        level,
        icon,
      } = alarmItem;
      const { mac, productKey } = device;
      const alarmInfo = {
        action,
        type,
        name,
        icon,
        level,
        time: moment().valueOf(),
        device: { mac, productKey },
      };
      alarmInfoList.push(alarmInfo);
    }
  }
  return alarmInfoList;
}

function getDefaultData() {
  return { data: {}, faults: {}, alerts: {} };
}

export interface IDeviceDataModel {
  data: { [deviceId: string]: IData };
  alarmQueue: any;
  deviceReportData(payload: IReportPayload): void;
  pushAlarmInfoToQueue(alarmList: any): any;
  delteAlarmInfo(alarmItem: any): boolean;
  cleanData(): void;
}

class DeviceData implements IDeviceDataModel {
  constructor() {
    makeAutoObservable(this);
  }

  @persist('object') data: { [deviceId: string]: IData } = {};

  alarmQueue = [];

  deviceFirstRoute: string | undefined = undefined;

  // 告警信息队列
  cmdStack: any = {}; // 告警信息队列

  routePath: string | null = null;

  deviceReportData(payload: IReportPayload) {
    console.log('deviceReportData', payload);
    const { device, alerts, faults, data, fromCmd } = payload;
    const deviceId = getDeviceId(device);
    const deviceData = toJS(this.data[deviceId]);

    const obj = {
      ...filterDeviceData(deviceData, device, data, alerts, faults),
    };
    obj[deviceId].loading = false;
    obj[deviceId].fromCmd = fromCmd;
    const isInAdvance = fromCmd === true; // 是否为预设操作
    if (!isInAdvance) {
      const deviceCmdCacheStack = this.cmdStack[deviceId];
      if (deviceCmdCacheStack && deviceCmdCacheStack.timeoutHandle) {
        clearTimeout(deviceCmdCacheStack.timeoutHandle);
      }
      this.cmdStack[deviceId] = undefined; // 有数据正常上报就清空指令栈
    } else {
      obj[deviceId].sourceData = data;
    }
    // 比较是否需要更新
    if (!isEqual(deviceData, obj[deviceId])) {
      if (global.token) {
        // 未登陆，不判断是否触发报警
        const { alarmConfig } = getItemForPk(device.productKey);
        if (alarmConfig && alarmConfig.alarms) {
          const alarmInfoList = getAlarmInfo(alarmConfig, obj[deviceId], device);
          const oldAlarmInfoList = this.data[deviceId] ? this.data[deviceId].alarmInfo || [] : [];

          // 新增的报警列表
          const alarmList = alarmInfoList.filter(
            i => oldAlarmInfoList.findIndex(o => o.name === i.name) < 0 && i.action && i.action.type === 'popup'
          );
          if (alarmList && alarmList.length > 0) {
            console.log('新报警，加入告警队列');
            this.pushAlarmInfoToQueue(alarmList);
          } else {
            console.log('重复报警了，不更新');
          }
          obj[deviceId].alarmInfo = alarmInfoList as any;
        }
      }
      console.log('更新 deviceData');
      set(this.data, deviceId, obj[deviceId]);
      if (deviceDetailModel.device && isSameDevice(deviceDetailModel.device, device)) {
        // 如果详情有设备，就更新数据进去
        deviceDetailModel.updateDeviceData(this.data[deviceId]);
      }
    }
  }

  // 如果需要给设备设置默认数据，可以使用这个方法，一般是体验中心使用比较多
  setExperienceData({ device, data }) {
    this.setDeviceDataInAdvance({ device, data, noTimeout: true });
  }

  setDeviceDataInAdvance({ device, data, noTimeout = false }: { device: IDevice; data: any; noTimeout: boolean }) {
    // 发送指令时预设设备数据点
    const deviceId = getDeviceId(device);
    const oldData = { ...this.data[deviceId] };
    this.deviceReportData({ device, data, fromCmd: true });
    if (!noTimeout) {
      const deviceCmdCacheStack = this.cmdStack[deviceId] || {};
      if (!deviceCmdCacheStack.cacheData) {
        // 记录上一次上报数据，超时的话，需要次数据恢复操作前的状态
        deviceCmdCacheStack.cacheData = oldData;
      }
      if (!device.isLowPower) {
        const timeoutHandle = setTimeout(() => {
          this.recoverDeviceData(device);
        }, 15 * 1000); // 15s后，设备还没上报数据，则恢复状态
        if (deviceCmdCacheStack.timeoutHandle) {
          clearTimeout(deviceCmdCacheStack.timeoutHandle);
        }
        deviceCmdCacheStack.timeoutHandle = timeoutHandle;
      }

      this.cmdStack[deviceId] = deviceCmdCacheStack;
    }
  }

  recoverDeviceData(device: IDevice) {
    console.log('recoverDeviceData');
    const deviceId = getDeviceId(device);
    const deviceCmdCacheStack = this.cmdStack[deviceId];
    if (deviceCmdCacheStack && deviceCmdCacheStack.cacheData) {
      this.deviceReportData({
        ...deviceCmdCacheStack.cacheData,
        device,
        fromCmd: true,
      });
    }
    if (deviceCmdCacheStack.timeoutHandle) {
      clearTimeout(deviceCmdCacheStack.timeoutHandle);
    }
    this.cmdStack[deviceId] = undefined;
    if (global.deviceNotResponseModal) {
      global.deviceNotResponseModal.show();
    }
  }

  pushAlarmInfoToQueue(alarmList: any) {
    // 告警信息推入队列
    // 告警大于故障，再等级排序，再按时间发生顺序排序
    this.alarmQueue = this.alarmQueue.concat(alarmList).sort(alramInfoSort);
  }

  delteAlarmInfo(alarmItem: any) {
    // 从队列中删除告警信息
    const index = this.alarmQueue.findIndex(i => isSameAlarm(alarmItem, i));
    if (index > -1) {
      console.log('delteAlarmInfo');
      this.alarmQueue.splice(index, 1);
      this.alarmQueue = [...this.alarmQueue];
      return true;
    }
    return false;
  }

  /**
   * 手动清空故障队列，200ms后手动恢复
   */
  async tempChangeAlarmQueue() {
    const tempQueue = cloneDeep(this.alarmQueue);
    this.alarmQueue = [];
    await sleep(200);
    this.alarmQueue = tempQueue;
  }

  changeAlarmQueue() {
    console.log('退出模版返回主体App后触发告警队列更新');
    this.alarmQueue = [...this.alarmQueue];
  }

  handleAlarmQueue(openType: string, path: string) {
    const needResetAlarmQueue =
      this.routePath && templatesRegExp.test(this.routePath) && pageRegExp.test(path) && openType === 'navigateBack';
    if (needResetAlarmQueue) {
      this.changeAlarmQueue();
    }
    this.routePath = path;
  }

  initDeviceData(device: IDevice) {
    const deviceId = getDeviceId(device);
    set(this.data, deviceId, getDefaultData());
  }

  getRoute(wxTemplateId: string) {
    // 因为h5 和小程序混合开发的原因，需要在这里获取正确的路由
    let first = `pages/home/index`;
    templateRouter.map(item => {
      if (item.root.indexOf(wxTemplateId) !== -1) {
        first = `${item.pages[0]}`;
      }
    });
    return `/templates/${wxTemplateId}/${first}`;
  }

  getDeviceRoute(productConfig: IProduct) {
    let wxTemplateId = productConfig.wxTemplateId ?? defaultTemplateRoute;
    if (productConfig.useOtherProductTemplate) {
      wxTemplateId = getItemForPk(productConfig.useOtherProductTemplate).wxTemplateId;
    }
    return this.getRoute(wxTemplateId);
  }

  goToDetailPage(device: IDevice) {
    const deviceId = getDeviceId(device);
    deviceDetailModel.cleanData();
    deviceDetailModel.updateDeviceDetail(device);
    deviceDetailModel.updateDeviceData(this.data[deviceId] || getDefaultData());
    const productConfig = getItemForPk(device.productKey);
    if (!device.isVirtual) {
      SDK.getDeviceStatus({ device });
      checkoutDeviceNeedConnect(device);
      if (haveDeviceTimeCorrect(productConfig)) {
        if (device && device.netStatus === 2) {
          gizwitsSdk.sendTimeCorrectCmd(device);
        }
      }
    }

    this.deviceFirstRoute = this.getDeviceRoute(productConfig);
    (wx.onAppRoute as OnAppRoute.Listener)(({ openType, path }) => {
      this.handleAlarmQueue(openType, path);
    });
    Taro.navigateTo({
      url: this.deviceFirstRoute,
      complete: res => {
        // console.log(res);
        if (res.errMsg.includes('not found')) {
          console.log('没有找到这个模板路由，尝试跳Auto');
          if (this.deviceFirstRoute !== defaultTemplateRoute) {
            Taro.navigateTo({ url: this.getRoute(defaultTemplateRoute) });
          }
        }
      },
    });
    alarmPush.subscribesDeviceWXMessage(device);
  }

  goToVirtualDeviceDetailPage(product: IProduct) {
    const name = ProductConfig.getProductNameFromConfig(languageModel.key, product, true);
    const device = {
      isVirtual: true,
      productKey: product.productKey,
      name: name,
      netStatus: 2,
      isOnline: true,
      did: `${product.productKey}virtual`,
      mac: `${product.productKey}virtual`,
    };
    this.goToDetailPage(device);
  }

  // 跳出整个模板页面，不管当前在设备的哪一层页面
  allBackToHome() {
    if (this.deviceFirstRoute) {
      const routers = Taro.getCurrentPages();
      const deviceRoute = this.deviceFirstRoute.substring(1, this.deviceFirstRoute.length);
      const index = routers.findIndex(i => i.route.includes(deviceRoute));
      if (index > -1) {
        // 确实在模板页面里面了
        const delta = routers.length - index;
        Taro.navigateBack({ delta });
      }
    }
  }

  cleanData() {
    this.data = {};
    this.alarmQueue = [];
  }

  async logRecord(payload: ILogRecordPayload) {
    const {
      size = 500,
      current = 0,
      beginTime,
      endTime,
      dataPoints = [],
      logType = 1,
      device: { mac, productKey },
    } = payload;
    const params = {
      size,
      current,
      query: {
        logType,
        mac,
        productKey,
        beginTime,
        endTime,
      },
    };
    const data = await logPage(params);
    console.log(data);
    if (data.success) {
      data.data.records.map((item: any) => (item.content = JSON.parse(item.content)));
      if (dataPoints && dataPoints.length > 0 && data.data && data.data.records.length > 0) {
        const newRecords = data.data.records.filter((record: any) => {
          // eslint-disable-next-line no-restricted-syntax
          for (const dataPoint of dataPoints) {
            // 有一个数据点满足条件就行
            if (record.content[dataPoint.key] === dataPoint.value) {
              return true;
            }
          }
          return false;
        });
        data.data.records = newRecords;
      }
    }
    console.log(data);
    return data;
  }
}

interface ILogRecordPayload {
  size?: number;
  current?: number;
  beginTime: string;
  endTime: string;
  dataPoints?: [{ [key: string]: any }];
  logType?: number;
  device: IDevice;
}

const DeviceData_Store_Key = 'deviceData';
export { DeviceData, DeviceData_Store_Key };

export default new DeviceData();
